#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#include <libxml2/libxml/tree.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <unistd.h>

#include "compat.h"

#include "psd_modules.h"
#include "psd_conn.h"
#include "psd_logger.h"
#include "psd_session.h"
#include "psd_policy.h"
#include "psd_path_handler.h"
#include "hashtable.h"
#include "psd_config.h"

#define DRAGON_DOWN		0x00
#define DRAGON_UP		0x01

typedef struct psd_dragon_path_t {
	int status;
	char *dragon_server;
	int dragon_port;
	char *peer_server;
	int peer_port;
	char *xml_request_file;
	xmlDocPtr reservation_resp;
	char *reservation_id;
	int reservation_size;
	uint16_t bandwidth, bandwidth_used;
} psdDragonPath;

int psd_dragon_shared_init();
static int psd_dragon_shared_allocate_path(const char *path_id, psdPath **ret_path);
static int psd_dragon_shared_new_channel(psdPath *path, uint16_t size, psdChannel **channel);
static int psd_dragon_shared_close_channel(psdPath *path, psdChannel *channel);
static int psd_dragon_shared_resize_channel(psdPath *path, psdChannel *channel, uint16_t new_size);
static void psd_dragon_shared_free_path(psdPath *path);

static int psd_dragon_request_path(const char *dragon_server, int dragon_port, const char *request_file, uint16_t size, char **ret_reservation_id, xmlDoc **reservation_resp, uint16_t *new_bandwidth);
static int psd_dragon_resize_path(const char *dragon_server, int dragon_port, const char *request_file, const char *reservation_id, uint16_t old_size, uint16_t new_size, char **ret_reservation_id, xmlDoc **reservation_resp, uint16_t *new_bandwidth);
static int psd_dragon_close_path(const char *dragon_server, int dragon_port, const char *reservation_id);

static int __psd_dragon_shared_new_channel(psdPath *path, uint16_t size, psdChannel **new_channel);
static int __psd_dragon_shared_resize_channel(psdPath *path, psdChannel *channel, uint16_t new_size);
static int __psd_dragon_shared_close_channel(psdPath *path, psdChannel *channel);

static psdDragonPath *psd_alloc_dragon_path();
static void psd_free_dragon_path(psdDragonPath *pi);
static char *dragon_get_ast_id(xmlNodePtr root);
static char *dragon_get_status(xmlNodePtr root);
static int dragon_get_bandwidth(xmlNodePtr root, uint16_t *ret_bandwidth);
static int dragon_set_bandwidth(xmlNodePtr root, uint16_t bandwidth);
static xmlNodePtr __libxml_find_node(xmlNodePtr root, const char *label, int max_depth);

static int dragon_bw2int(const char *bandwidth, uint16_t *ret_bandwidth);
static int dragon_int2bw(uint16_t bandwidth, const char **ret_bandwidth);

static int def_dragon_port = 5008;

psdModule psd_dragon_shared_module = {
	.desc = "DRAGON Module",
	.dependencies = "",
	.init = psd_dragon_shared_init
};

psdPathHandler psd_dragon_shared_path_handler = {
	.name = "DRAGON",
	.allocate = psd_dragon_shared_allocate_path, 
};

psdModule *module_info() {
	return &psd_dragon_shared_module;
}

int psd_dragon_shared_init() {
	return psd_add_path_handler(&psd_dragon_shared_path_handler);
}

static int psd_dragon_shared_allocate_path(const char *path_id, psdPath **ret_path) {
	psdPath *path;
	char *astb_server;
	int astb_port;
	char *xml_file;
	psdDragonPath *pi;

	if (psd_depot_settings_get_4("paths", path_id, "dragon", "astb_host", &astb_server) != 0) {
		psd_err(0, "no ASTB host specified");
		goto error_exit;
	}

	if (psd_depot_settings_get_int_4("paths", path_id, "dragon", "astb_port", &astb_port) != 0) {
		psd_info(0, "no astb port specified: using the default: %d", def_dragon_port);
		astb_port = def_dragon_port;
	}

	if (psd_depot_settings_get_4("paths", path_id, "dragon", "xml_request_file", &xml_file) != 0) {
		psd_err(0, "no xml request file specified");
		goto error_exit;
	}

	path = psd_alloc_path();
	if (!path)
		goto error_exit;

	pi = psd_alloc_dragon_path();
	if (!pi)
		goto error_exit_path;

	pi->dragon_server = astb_server;
	pi->dragon_port = astb_port;
	pi->xml_request_file = xml_file;

	path->path_private = pi;
	path->new_channel = psd_dragon_shared_new_channel;
	path->resize_channel = psd_dragon_shared_resize_channel;
	path->close_channel = psd_dragon_shared_close_channel;
	path->free = psd_dragon_shared_free_path;

	*ret_path = path;

	return 0;

error_exit_path:
	psd_free_path(path);
error_exit:
	return -1;
}

static int psd_dragon_shared_new_channel(psdPath *path, uint16_t size, psdChannel **channel) {
	int retval;

	pthread_mutex_lock(&(path->lock));
	{
		retval = __psd_dragon_shared_new_channel(path, size, channel);
	}
	pthread_mutex_unlock(&(path->lock));

	return retval;
}

static int psd_dragon_shared_close_channel(psdPath *path, psdChannel *channel) {
	int retval;

	pthread_mutex_lock(&(path->lock));
	{
		retval = __psd_dragon_shared_close_channel(path, channel);
	}
	pthread_mutex_unlock(&(path->lock));

	return retval;
}

static int psd_dragon_shared_resize_channel(psdPath *path, psdChannel *channel, uint16_t new_size) {
	int retval;

	pthread_mutex_lock(&(path->lock));
	{
		retval = __psd_dragon_shared_resize_channel(path, channel, new_size);
	}
	pthread_mutex_unlock(&(path->lock));

	return retval;
}

static int __psd_dragon_shared_new_channel(psdPath *path, uint16_t size, psdChannel **new_channel) {
	xmlDocPtr doc;
	char *reservation_id;
	psdDragonPath *pi = path->path_private;
	psdChannel *channel;
	uint16_t new_bandwidth;

	psd_info(10, "%s: allocating new channel of size: %d", path->description, size);

	channel = psd_alloc_channel();
	if (!channel) {
		psd_err(0, "%s: couldn't allocate channel object", path->description);
		goto error_exit;
	}

	if (pi->status == DRAGON_DOWN) {
		psd_info(0, "%s: the DRAGON path is down, allocating a new one", path->description);

		if (psd_dragon_request_path(pi->dragon_server, pi->dragon_port, pi->xml_request_file, size, &reservation_id, &doc, &new_bandwidth) != 0) {
			psd_err(0, "%s: couldn't allocate DRAGON path", path->description);
			goto error_exit_channel;
		}

		psd_info(10, "%s: allocated new path of size %d Mbit/s. Id: %s", path->description, new_bandwidth, reservation_id);

		// save the path information
		pi->reservation_id = reservation_id;
		pi->reservation_resp = doc;
		pi->bandwidth = new_bandwidth;

		pi->status = DRAGON_UP;
	} else if (pi->bandwidth < pi->bandwidth_used + size) {
		psd_info(0, "%s: resizing path from %d to %d", path->description, pi->bandwidth, pi->bandwidth_used + size);

		if (psd_dragon_resize_path(pi->dragon_server, pi->dragon_port, pi->xml_request_file, pi->reservation_id, pi->bandwidth, pi->bandwidth_used + size, &reservation_id, &doc, &new_bandwidth) != 0) {
			psd_err(0, "%s: couldn't resize path from %d to %d", path->description, pi->bandwidth, pi->bandwidth_used + size);
			goto error_exit_channel;
		}

		psd_info(10, "%s: path resized to %d Mbit/s. New id: %s", path->description, new_bandwidth, reservation_id);

		free(pi->reservation_id);
		xmlFreeDoc(pi->reservation_resp);

		pi->reservation_id = reservation_id;
		pi->reservation_resp = doc;
		pi->bandwidth = new_bandwidth;
	} else {
		psd_info(0, "%s: reusing existing path. Amount used: %d/%d", path->description, pi->bandwidth_used, pi->bandwidth);
	}

	pi->bandwidth_used += size;

	// set the channels bandwidth
	channel->bandwidth = size;

	// add the channel to the path's list of channels
	LIST_INSERT_HEAD(&(path->channel_list), channel, path_entries);

	*new_channel = channel;

	psd_info(10, "%s: allocated new channel of size: %d", path->description, channel->bandwidth);

	return 0;

error_exit_channel:
	psd_free_channel(channel);
error_exit:
	return -1;
}

static int __psd_dragon_shared_resize_channel(psdPath *path, psdChannel *channel, uint16_t new_size) {
	psd_err(0, "channel resizing not supported");
	return -1;
}

static int __psd_dragon_shared_close_channel(psdPath *path, psdChannel *channel) {
	psdDragonPath *pi = path->path_private;
	psdChannel *curr_channel;

	psd_info(0, "%s: shutting down channel", path->description);

	// verify that the channel past is actually in the given path
	for(curr_channel = path->channel_list.lh_first; curr_channel != NULL; curr_channel = curr_channel->path_entries.le_next) {
		if (curr_channel == channel)
			break;
	}

	// if not, error out
	if (curr_channel == NULL) {
		psd_err(0, "%s: tried to close a channel from a different path", path->description);
		goto error_exit;
	}

	// remove the channel from the list of channels
	LIST_REMOVE(channel, path_entries);

	pi->bandwidth_used -= channel->bandwidth;

	// if we have removed the last channel, close the path down
	if (path->channel_list.lh_first == NULL) {
		psd_info(10, "%s: no more channels, shutting down path", path->description);

		if (psd_dragon_close_path(pi->dragon_server, pi->dragon_port, pi->reservation_id) != 0) {
			psd_warn(0, "__psd_dragon_shared_close_channel(%s): failed to close path", path->description);
		} else {
			psd_info(10, "%s: successfully shutdown path", path->description);

			// reset the  path info
			xmlFreeDoc(pi->reservation_resp);
			free(pi->reservation_id);
			pi->reservation_resp = NULL;
			pi->reservation_id = NULL;
			pi->bandwidth_used = 0;
			pi->bandwidth = 0;
			pi->status = DRAGON_DOWN;
		}
	}

	// remove all the connections from the channel list (though, they should be gone already... hrmmm...)
	if (channel->connlist.lh_first != NULL) {
		psd_warn(0, "__psd_dragon_shared_close_channel(%s): closing channel with outstanding connections", path->description);

		while(channel->connlist.lh_first != NULL) {
			psdConn *conn = channel->connlist.lh_first;

			conn->channel = NULL;
			LIST_REMOVE(conn, channel_entries);
		}
	}

	psd_free_channel(channel);

	psd_info(10, "%s: successfully shutdown channel", path->description);

	return 0;

error_exit:
	return -1;
}

static int psd_dragon_request_path(const char *dragon_server, int dragon_port, const char *request_file, uint16_t size, char **ret_reservation_id, xmlDoc **reservation_resp, uint16_t *new_bandwidth) {
	char tmp[255];
	char buf[2048];
	int sd;
	int amt_recvd;
	xmlDocPtr req_doc, resp_doc;
	xmlNodePtr root;
	char *ast_status;
	char *reservation_id;
	int n;
	uint16_t bandwidth;
	xmlChar *xml_buf;
	int xml_buf_size;
	int amt_sent;
	struct timeval stime, etime;

	gettimeofday(&stime, NULL);

	req_doc = xmlParseFile(request_file);
	if (!req_doc) {
		psd_err(0, "error parsing request file %s", request_file);
		goto error_exit;
	}

	root = xmlDocGetRootElement(req_doc);
	if (!root) {
		psd_err(0, "invalid XML document in file %s: no root element", request_file);
		goto error_exit_req_doc;
	}

	if (size != 0) {
		if (dragon_set_bandwidth(root, size) != 0) {
			psd_warn(0, "couldn't set the requested bandwidth to %d", size);
		}
	}

	xmlDocDumpFormatMemory(req_doc, &xml_buf, &xml_buf_size, 0);

	snprintf(tmp, 255, "%s/%d", dragon_server, dragon_port);

	sd = xsp_make_connection(tmp);
	if (sd < 0) {
		psd_err(0, "couldn't connect to %s/%d", dragon_server, dragon_port);
		goto error_exit_xml_buf;
	}

	psd_info(10, "sending request: \"%s\"", xml_buf);

	amt_sent = 0;
	do {
		n = send(sd, xml_buf + amt_sent, xml_buf_size - amt_sent, 0);
		if (n <= 0) {
			psd_err(0, "failed to send ASTB request to %s/%d", dragon_server, dragon_port);
			goto error_exit_sd;
		}

		amt_sent += n;
	} while (amt_sent < xml_buf_size);

	bzero(buf, sizeof(buf));

	amt_recvd = 0;

	do {
		n = recv(sd, buf + amt_recvd, sizeof(buf) - amt_recvd, 0);
		if (n < 0) {
			psd_err(0, "failed to receive ASTB response from %s/%d", dragon_server, dragon_port);
			goto error_exit_sd;
		}

		amt_recvd += n;
	} while(n > 0);

	if (amt_recvd == 0)
		goto error_exit_sd;

	psd_info(10, "response received: \"%s\"", buf);

	resp_doc = xmlParseMemory(buf, strlen(buf));
	if (!resp_doc) {
		psd_err(0, "malformed XML document received");
		goto error_exit_sd;
	}

	root = xmlDocGetRootElement(resp_doc);
	if (!root) {
		psd_err(0, "invalid XML document received: no root element");
		goto error_exit_resp_doc;
	}

	ast_status = dragon_get_status(root);
	if (!ast_status) {
		psd_err(0, "invalid XML document received: no status element");
		goto error_exit_resp_doc;
	}

	if (strcmp(ast_status, "AST_SUCCESS") != 0) {
		psd_err(0, "ASTB path setup failed");
		goto error_exit_status;
	}

	reservation_id = dragon_get_ast_id(root);
	if (!reservation_id) {
		psd_err(0, "invalid XML document received: no ast id");
		goto error_exit_status;
	}

	if (dragon_get_bandwidth(root, &bandwidth) != 0) {
		psd_err(0, "invalid XML document received: no bandwidth");
		goto error_exit_id;
	}

	psd_info(0, "reservation: %s size: %d Mbit/s", reservation_id, bandwidth);

	*ret_reservation_id = reservation_id;
	*reservation_resp = resp_doc;
	*new_bandwidth = bandwidth;

	close(sd);
	free(ast_status);
	xmlFree(xml_buf);
	xmlFreeDoc(req_doc);

	gettimeofday(&etime, NULL);

	psd_info(0, "time to allocate path: %f", difftv(&stime, &etime));

	return 0;

error_exit_id:
	free(reservation_id);
error_exit_status:
	free(ast_status);
error_exit_resp_doc:
	xmlFreeDoc(resp_doc);
error_exit_sd:
	close(sd);
error_exit_xml_buf:
	xmlFree(xml_buf);
error_exit_req_doc:
	xmlFreeDoc(req_doc);
error_exit:
	return -1;
}

static int psd_dragon_resize_path(const char *dragon_server, int dragon_port, const char *request_file, const char *reservation_id, uint16_t old_size, uint16_t new_size, char **ret_reservation_id, xmlDoc **reservation_resp, uint16_t *new_bandwidth) {
	struct timeval stime, etime;

	gettimeofday(&stime, NULL);

	if (psd_dragon_close_path(dragon_server, dragon_port, reservation_id) != 0) {
		psd_err(0, "couldn't close existing path");
		goto error_exit;
	}

	if (psd_dragon_request_path(dragon_server, dragon_port, request_file, new_size, ret_reservation_id, reservation_resp, new_bandwidth) != 0) {
		psd_err(0, "couldn't open path with new size");
		goto error_exit_path;
	}

	gettimeofday(&etime, NULL);

	psd_info(0, "time to resize: %f", difftv(&stime, &etime));

	return 0;

error_exit_path:
	if (psd_dragon_request_path(dragon_server, dragon_port, request_file, old_size, ret_reservation_id, reservation_resp, new_bandwidth) != 0) {
		psd_err(0, "closed existing path, couldn't recreate it. You didn't *need* that path did you...");
	}
error_exit:
	return -1;
}
static int psd_dragon_close_path(const char *dragon_server, int dragon_port, const char *reservation_id) {
	int sd, n;
	char buf[1024];
	char tmp[255];
	int amt_recvd;
	xmlDocPtr doc;
	xmlNodePtr root;
	char *ast_status;
	struct timeval stime, etime;

	gettimeofday(&stime, NULL);

	snprintf(tmp, 255, "%s/%d", dragon_server, dragon_port);
	sd = xsp_make_connection(tmp);
	if (sd < 0) {
		psd_err(0, "couldn't connect to %s/%d", dragon_server, dragon_port);
		goto error_exit;
	}

	snprintf(buf, sizeof(buf), "<topology action=\"RELEASE_REQ\" ast_id=\"%s\" />", reservation_id);

	psd_info(10, "sending: \"%s\"", buf);

	n = send(sd, buf, strlen(buf), 0);
	if (n < strlen(buf)) {
		psd_err(0, "couldn't send tear down message");
		goto error_exit_sd;
	}

	bzero(buf, sizeof(buf));

	amt_recvd = 0;

	do {
		n = recv(sd, buf + amt_recvd, sizeof(buf) - amt_recvd, 0);
		if (n > 0) {
			amt_recvd += n;
		}
	} while(n > 0);

	psd_info(10, "received: \"%s\"", buf);

	doc = xmlParseMemory(buf, strlen(buf));
	if (!doc) {
		psd_err(0, "malformed response received");
		goto error_exit_sd;
	}

	root = xmlDocGetRootElement(doc);
	if (!root) {
		psd_err(0, "invalid response received: no root element");
		goto error_exit_doc;
	}

	ast_status = dragon_get_status(root);
	if (!ast_status) {
		psd_err(0, "invalid response received: no status element");
		goto error_exit_doc;
	}

	if (strcmp(ast_status, "AST_SUCCESS") != 0) {
		psd_err(0, "ASTB path teardown failed");
		goto error_exit_status;
	}

	close(sd);
	xmlFreeDoc(doc);
	xmlFree(ast_status);

	gettimeofday(&etime, NULL);

	psd_info(0, "time to shutdown path: %f", difftv(&stime, &etime));

	return 0;

error_exit_status:
	xmlFree(ast_status);
error_exit_doc:
	xmlFreeDoc(doc);
error_exit_sd:
	close(sd);
error_exit:
	return -1;
}

static xmlNodePtr __libxml_find_node(xmlNodePtr root, const char *label, int max_depth) {
	xmlNodePtr curr, ret_node;

	for (curr = root; curr != NULL; curr = curr->next) {
		if (strcmp(label, (char *) curr->name) == 0) {
			return curr;
		}

		if (max_depth != 0) {
			ret_node = __libxml_find_node(curr->children, label, max_depth - 1);
			if (ret_node != NULL)
				return ret_node;
		}
	}

	return NULL;
}

static char *dragon_get_ast_id(xmlNodePtr root) {
	xmlNodePtr ast_node;
	xmlChar *ast_id;

	ast_node = __libxml_find_node(root, "topology", -1);
	if (!ast_node)
		return NULL;

	ast_id = xmlGetProp(ast_node, (xmlChar *) "ast_id");
	
	return (char *) ast_id;
}

static int dragon_set_bandwidth(xmlNodePtr root, uint16_t bandwidth) {
	xmlNodePtr node;
	const char *bw;

	if (dragon_int2bw(bandwidth, &bw) != 0)
		return -1;

	node = __libxml_find_node(root, "bandwidth", -1);
	if (!node)
		return -1;

	// XXX: xmlEncodeEntitiesReentrant()
	xmlNodeSetContent(node, bw);

	return 0;
}

static int dragon_get_bandwidth(xmlNodePtr root, uint16_t *ret_bandwidth) {
	xmlNodePtr node;

	node = __libxml_find_node(root, "bandwidth", -1);
	if (!node)
		return -1;

	if (dragon_bw2int(node->children->content, ret_bandwidth) != 0)
		return -1;

	return 0;
}

static int dragon_bw2int(const char *bandwidth, uint16_t *ret_bandwidth) {
	uint16_t bwint;

	if (strcmp(bandwidth, "gige") == 0) {
		bwint = 1000;
	} else if (strcmp(bandwidth, "gige_f") == 0) {
		bwint = 1250;
	} else if (strcmp(bandwidth, "2gige") == 0) {
		bwint = 2000;
	} else if (strcmp(bandwidth, "3gige") == 0) {
		bwint = 3000;
	} else if (strcmp(bandwidth, "4gige") == 0) {
		bwint = 4000;
	} else if (strcmp(bandwidth, "5gige") == 0) {
		bwint = 5000;
	} else if (strcmp(bandwidth, "6gige") == 0) {
		bwint = 6000;
	} else if (strcmp(bandwidth, "7gige") == 0) {
		bwint = 7000;
	} else if (strcmp(bandwidth, "8gige") == 0) {
		bwint = 8000;
	} else if (strcmp(bandwidth, "9gige") == 0) {
		bwint = 9000;
	} else if (strcmp(bandwidth, "eth100M") == 0) {
		bwint = 100;
	} else if (strcmp(bandwidth, "eth200M") == 0) {
		bwint = 200;
	} else if (strcmp(bandwidth, "eth300M") == 0) {
		bwint = 300;
	} else if (strcmp(bandwidth, "eth400M") == 0) {
		bwint = 400;
	} else if (strcmp(bandwidth, "eth500M") == 0) {
		bwint = 500;
	} else if (strcmp(bandwidth, "eth600M") == 0) {
		bwint = 600;
	} else if (strcmp(bandwidth, "eth700M") == 0) {
		bwint = 700;
	} else if (strcmp(bandwidth, "eth800M") == 0) {
		bwint = 800;
	} else if (strcmp(bandwidth, "eth900M") == 0) {
		bwint = 900;
	} else if (strcmp(bandwidth, "10g") == 0) {
		bwint = 10000;
	} else if (strcmp(bandwidth, "hdtv") == 0) {
		bwint = 1485;
	} else if (strcmp(bandwidth, "oc48") == 0) {
		bwint = 2488;
	} else {
		goto error_exit;
	}

	*ret_bandwidth = bwint;

	return 0;

error_exit:
	return -1;
}

static int dragon_int2bw(uint16_t bandwidth, const char **ret_bandwidth) {
	char *bw;

	if (bandwidth <= 100) {
		bw = "eth100M";
	} else if (bandwidth <= 200) {
		bw = "eth200M";
	} else if (bandwidth <= 300) {
		bw = "eth300M";
	} else if (bandwidth <= 400) {
		bw = "eth400M";
	} else if (bandwidth <= 500) {
		bw = "eth500M";
	} else if (bandwidth <= 600) {
		bw = "eth600M";
	} else if (bandwidth <= 700) {
		bw = "eth700M";
	} else if (bandwidth <= 800) {
		bw = "eth800M";
	} else if (bandwidth <= 900) {
		bw = "eth900M";
	} else if (bandwidth <= 1000) {
		bw = "gige";
	} else if (bandwidth <= 1250) {
		bw = "gige_f";
	} else if (bandwidth <= 1485) {
		bw = "hdtv";
	} else if (bandwidth <= 2000) {
		bw = "2gige";
	} else if (bandwidth <= 2488) {
		bw = "oc48";
	} else if (bandwidth <= 3000) {
		bw = "3gige";
	} else if (bandwidth <= 4000) {
		bw = "4gige";
	} else if (bandwidth <= 5000) {
		bw = "5gige";
	} else if (bandwidth <= 6000) {
		bw = "6gige";
	} else if (bandwidth <= 7000) {
		bw = "7gige";
	} else if (bandwidth <= 8000) {
		bw = "8gige";
	} else if (bandwidth <= 9000) {
		bw = "9gige";
	} else if (bandwidth <= 10000) {
		bw = "10g";
	} else {
		goto error_exit;
	}

	*ret_bandwidth = bw;

	return 0;

error_exit:
	return -1;
}

static char *dragon_get_status(xmlNodePtr root) {
	xmlNodePtr status_node;

	status_node = __libxml_find_node(root, "status", 1);
	if (!status_node) {
		return NULL;
	}

	return (char *) strdup((char *) status_node->children->content);
}

static psdDragonPath *psd_alloc_dragon_path() {
	psdDragonPath *pi ;

	pi = malloc(sizeof(psdDragonPath));
	if (!pi) {
		goto error_exit;
	}

	bzero(pi, sizeof(psdDragonPath));

	return pi;

error_exit:
	return NULL;
}

static void psd_free_dragon_path(psdDragonPath *pi) {
	if (pi->dragon_server)
		free(pi->dragon_server);
	if (pi->peer_server)
		free(pi->peer_server);
	if (pi->xml_request_file)
		free(pi->xml_request_file);
	if (pi->reservation_resp)
		xmlFreeDoc(pi->reservation_resp);
	if (pi->reservation_id)
		free(pi->reservation_id);
	free(pi);
}

static void psd_dragon_shared_free_path(psdPath *path) {
	psd_free_dragon_path((psdDragonPath *) path->path_private);
	psd_free_path(path);
}
